package com.hsgene.order.service.impl;

import com.google.common.collect.Maps;
import com.hsgene.common.ClientType;
import com.hsgene.common.DeleteFlagType;
import com.hsgene.common.util.basic.JsonUtils;
import com.hsgene.common.util.basic.UUID32;
import com.hsgene.common.util.collection.SetUtils;
import com.hsgene.common.util.result.ApiResult;
import com.hsgene.common.util.result.NullObject;
import com.hsgene.common.util.support.LimitHelper;
import com.hsgene.common.util.support.OrderHelper;
import com.hsgene.common.util.support.Pagination;
import com.hsgene.common.util.validation.FieldChecker;
import com.hsgene.generator.dto.GeneratorDto;
import com.hsgene.order.condition.OrderRefundCondition;
import com.hsgene.order.constants.v1.OrderPackageStatus;
import com.hsgene.order.constants.v1.OrderStatus;
import com.hsgene.order.domain.v1.GeneticOrderOperationDetail;
import com.hsgene.order.domain.v1.GeneticTestingOrder;
import com.hsgene.order.domain.v1.GeneticTestingOrderPackage;
import com.hsgene.order.domain.v1.GeneticTestingOrderRefund;
import com.hsgene.order.dto.console.*;
import com.hsgene.order.exception.OrderErrorCode;
import com.hsgene.order.persistence.OrderPackageRepository;
import com.hsgene.order.persistence.OrderRefundRepository;
import com.hsgene.order.persistence.OrderRepository;
import com.hsgene.order.service.OrderRefundService;
import com.hsgene.order.service.OrderService;
import com.hsgene.order.service.api.GeneratorService;
import com.qiniu.util.Json;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 订单退款ServiceImpl
 *
 * @author wangbing
 * @version 1.0, 2018/11/12
 */
@Service
public class OrderRefundServiceImpl implements OrderRefundService {

    private final static Logger LOGGER = LoggerFactory.getLogger(OrderRefundServiceImpl.class);

    @Resource
    private OrderRepository orderRepository;

    @Resource
    private GeneratorService generatorService;

    @Resource
    private OrderService orderService;

    @Resource
    private OrderRefundRepository orderRefundRepository;

    @Resource
    private OrderPackageRepository orderPackageRepository;

    @Value("${genetic.testing.orders.refund.no_prefix}")
    private String refundNoPrefix;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult<NullObject> orderRefund(OrderRefundInfoDto refund, String operatorId) {
        // 参数校验
        FieldChecker.checkEmpty(refund, "退款信息");
        refund.checkRefund();
        // 订单状态校验
        GeneticTestingOrder order = orderRepository.findById(refund.getOrderId());
        if (order == null || DeleteFlagType.DELETE.equals(DeleteFlagType.getEnum(order.getDeleteFlag()))) {
            return ApiResult.fail(OrderErrorCode.ORDER_NOT_EXISTED);
        }
        ClientType orderOrigin = ClientType.getEnum(order.getOrderOrigin());
        if (orderOrigin.isGeneShop()) {
            return ApiResult.fail(OrderErrorCode.ORDER_REFUND_ORIGIN_NOT_SUPPORTED);
        }
        OrderStatus currentStatus = OrderStatus.getEnum(order.getStatus());
        if (!currentStatus.canRefund()) {
            return ApiResult.fail(OrderErrorCode.ORDER_REFUND_NOT_PERMISSION);
        }
        // 对比当前订单套餐与退款套餐信息
        List<GeneticTestingOrderPackage> packages = orderPackageRepository.queryOrderPackages(refund.getOrderId());
        List<GeneticTestingOrderPackage> availablePackages = packages.stream()
                .filter(e -> !OrderPackageStatus.END.equals(OrderPackageStatus.getEnum(e.getStatus())))
                .collect(Collectors.toList());
        Map<OrderRefundPackageDto, GeneticTestingOrderPackage> refundMap = Maps.newHashMap();
        // 需要操作退款的套餐[过滤不需要退款的套餐]
        Set<OrderRefundPackageDto> refundSet = refund.getRefundPackages().stream()
                .filter(e -> !BigDecimal.ZERO.equals(e.getRefundAmount()))
                .peek(key -> availablePackages.stream()
                        .filter(value -> value.getPackageId().equals(key.getPackageId()) && value.getCharges().compareTo(key.getRefundAmount()) >= 0)
                        .findFirst().ifPresent(value -> refundMap.put(key, value)))
                .collect(Collectors.toSet());
        if (refundSet.size() == 0) {
            return ApiResult.fail(OrderErrorCode.ORDER_REFUND_PACKAGE_ZERO);
        }
        if (refundSet.size() != refundMap.size()) {
            Set<OrderRefundPackageDto> diffSet = SetUtils.diff(refundSet, refundMap.keySet());
            String packageNames = diffSet.stream().map(OrderRefundPackageDto::getPackageName).collect(Collectors.joining(","));
            return ApiResult.failFormatTip(OrderErrorCode.ORDER_REFUND_PACKAGE_NO_PERMISSION, packageNames);
        }
        // 先行获取退款编号
        ApiResult<GeneratorDto> generatorResult = generatorService.numberGenerator(refundNoPrefix, null, null);
        if (!generatorResult.isSucc()) {
            LOGGER.error("获取订单退款编号失败：" + generatorResult.getDesc());
            return ApiResult.fail(generatorResult.getError());
        }
        // 操作退款
        Date operationTime = new Date();
        refundMap.forEach((key, value) -> {
            BigDecimal refundAmount = value.getRefundAmount();
            if (refundAmount == null) {
                refundAmount = BigDecimal.ZERO;
            }
            refundAmount = refundAmount.add(key.getRefundAmount());
            value.setRefundAmount(refundAmount);
            BigDecimal charges = value.getCharges().subtract(key.getRefundAmount());
            value.setCharges(charges);
            if (value.getCharges().compareTo(BigDecimal.ZERO) <= 0) {
                value.setStatus(OrderPackageStatus.END.getCode());
                value.setIsStop(1);
            }
            orderPackageRepository.updateForRefund(value);
        });
        refundSet.stream()
                .map(OrderRefundPackageDto::getRefundAmount)
                .reduce(BigDecimal::add)
                .ifPresent(totalRefundAmount -> {
                    // 记录退款信息
                    GeneticTestingOrderRefund record = new GeneticTestingOrderRefund();
                    record.setId(UUID32.randomUUIDString());
                    record.setOrderId(order.getId());
                    record.setRefundTradeNo(generatorResult.getData().getSerialNumber());
                    record.setRefundDateTime(operationTime);
                    record.setRefundReason(refund.getRefundReason());
                    record.setRefundMoney(totalRefundAmount);
                    record.setRefundStatus(OrderStatus.REFUND_ACCESS.getCode());
                    record.setCreateDateTime(operationTime);
                    record.setUpdateDateTime(operationTime);
                    record.setDeleteFlag(DeleteFlagType.NOT_DELETED.getCode());
                    record.setRefundVouchers(JsonUtils.objectToJson(refund.getRefundVouchers()));
                    record.setRefundDetails(JsonUtils.objectToJson(refundSet));
                    orderRefundRepository.save(record);
                    // 更新订单信息
                    BigDecimal refundAmount = order.getRefundAmount();
                    if (refundAmount == null) {
                        refundAmount = BigDecimal.ZERO;
                    }
                    refundAmount = refundAmount.add(totalRefundAmount);
                    order.setRefundAmount(refundAmount);
                    order.setUpdateDateTime(operationTime);
                    BigDecimal totalCharges = order.getTotalCharges().subtract(totalRefundAmount);
                    order.setTotalCharges(totalCharges);
                    /*if (totalCharges.compareTo(BigDecimal.ZERO) <= 0) {

                    }*/
                    // fix BUG[20862] 不比较订单总金额，因为根据业务流程可能出现订单总金额不等于各套餐金额之和
                    List<GeneticTestingOrderPackage> newPackages = orderPackageRepository.queryOrderPackages(refund.getOrderId());
                    List<GeneticTestingOrderPackage> notEndList = newPackages.stream()
                            .filter(e -> !OrderPackageStatus.END.equals(OrderPackageStatus.getEnum(e.getStatus())))
                            .collect(Collectors.toList());
                    if (CollectionUtils.isEmpty(notEndList)) {
                        order.setBeforeExceptionStatus(order.getStatus());
                        order.setStatus(OrderStatus.ORDER_CLOSE.getCode());
                        order.setStopReason(refund.getRefundReason());
                    }
                    orderRepository.updateOrderRefund(order);
                });
        // 记录操作日志
        GeneticOrderOperationDetail orderOperationDetail = new GeneticOrderOperationDetail();
        orderOperationDetail.setOrderId(refund.getOrderId());
        orderOperationDetail.setOrderType(1);
        orderOperationDetail.setOperatorId(operatorId);
        orderOperationDetail.setDescription("订单退款");
        orderOperationDetail.setStatus(currentStatus.getCode());
        orderService.orderOperationLog(orderOperationDetail);

        return ApiResult.succ();
    }

    @Override
    public ApiResult<Pagination<OrderRefundDto>> queryOrderRefunds(OrderRefundCondition condition, OrderHelper
            orderHelper, LimitHelper limitHelper) {
        FieldChecker.checkEmpty(limitHelper, "分页参数");
        int totalCount = orderRefundRepository.queryRefundCount(condition);
        Pagination<OrderRefundDto> pagination = Pagination.newInstance(limitHelper, totalCount);
        if (totalCount > 0 && totalCount > limitHelper.getOffset()) {
            List<OrderRefundDto> refunds = orderRefundRepository.queryRefund(condition, orderHelper, limitHelper);
            if (!CollectionUtils.isEmpty(refunds)) {
                int number = limitHelper.getOffset() + 1;
                for (OrderRefundDto refund : refunds) {
                    refund.setNumber(number++);
                    OrderStatus orderStatus = OrderStatus.getEnum(refund.getOrderStatus());
                    refund.setOrderStatusDesc(orderStatus.getDesc());
                }
            }
            pagination.setResult(refunds);
        }
        return ApiResult.succ(pagination);
    }

    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor =
            Exception.class)
    @Override
    public ApiResult<NullObject> auditOrderRefund(String refundId, Boolean access, String rejectReason, String
            operatorId) {
        FieldChecker.checkEmpty(refundId, "退款id");
        FieldChecker.checkEmpty(access, "审核动作access");
        if (!access) {
            FieldChecker.checkMaxLength(rejectReason, 255, "拒绝原因");
        }
        FieldChecker.checkEmpty(operatorId, "操作人id");

        GeneticTestingOrderRefund refund = orderRefundRepository.findById(refundId);
        if (refund == null || DeleteFlagType.DELETE.getCode() == refund.getDeleteFlag()) {
            return ApiResult.fail(OrderErrorCode.ORDER_REFUND_NOT_EXISTED);
        }
        OrderStatus refundStatus = OrderStatus.getEnum(refund.getRefundStatus());
        if (!OrderStatus.WAIT_REFUND_AUDIT.equals(refundStatus)) {
            if (OrderStatus.REFUND_ACCESS.equals(refundStatus) || OrderStatus.REFUND_REJECT.equals(refundStatus)) {
                return ApiResult.fail(OrderErrorCode.ORDER_REFUND_AUDIT_DUPLICATE);
            } else {
                throw new IllegalStateException("退款状态异常：" + refund.getRefundStatus());
            }
        }
        GeneticTestingOrder order = orderRepository.findById(refund.getOrderId());
        if (order == null || DeleteFlagType.DELETE.getCode() == order.getDeleteFlag()) {
            return ApiResult.fail(OrderErrorCode.ORDER_NOT_EXISTED);
        }
        OrderStatus orderStatus = OrderStatus.getEnum(order.getStatus());
        if (!OrderStatus.WAIT_REFUND_AUDIT.equals(orderStatus)) {
            return ApiResult.fail(OrderErrorCode.ORDER_STATUS_EXCEPTION, "订单状态[" + orderStatus.getCode() + ":" +
                    orderStatus.getDesc() + "]异常，不能进行审核操作");
        }

        Date now = new Date();
        OrderStatus targetStatus = access ? OrderStatus.REFUND_ACCESS : OrderStatus.REFUND_REJECT;
        order.setStatus(targetStatus.getCode());
        order.setRefundStatus(String.valueOf(targetStatus.getCode()));
        order.setUpdateDateTime(now);
        orderRepository.updateOrderRefund(order);

        refund.setRefundStatus(targetStatus.getCode());
        refund.setRefundDateTime(now);
        if (!access) {
            refund.setRejectRefundReason(rejectReason);
        }
        refund.setUpdateDateTime(now);
        refund.setAuditorId(operatorId);
        orderRefundRepository.updateRefund(refund);

        // 记录操作日志
        GeneticOrderOperationDetail orderOperationDetail = new GeneticOrderOperationDetail();
        orderOperationDetail.setOrderId(refund.getOrderId());
        orderOperationDetail.setOrderType(1);
        orderOperationDetail.setOperatorId(operatorId);
        if (access) {
            orderOperationDetail.setDescription("通过订单退款申请");
        } else {
            orderOperationDetail.setDescription("拒绝订单退款申请");
        }
        orderOperationDetail.setStatus(orderStatus.getCode());
        orderService.orderOperationLog(orderOperationDetail);

        return ApiResult.succ();
    }

    /**
     * @param orderId 订单id
     * @return com.hsgene.order.domain.v1.GeneticTestingOrderRefund
     * @description 查询退款审批记录
     * @author maodi
     * @createDate 2018/11/27 11:15
     */
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor =
            Exception.class)
    @Override
    public OrderRefundDetailsDto queryOrderRefundDetails(String orderId) {
        List<GeneticTestingOrderRefund> geneticTestingOrderRefunds = orderRefundRepository.queryOrderRefundDetails
                (orderId);
        OrderRefundDetailsDto orderRefundDetailsDto = new OrderRefundDetailsDto();
        List<RefundApprovalRecordDto> refundApprovalRecordDtos = new ArrayList<>();
        for (GeneticTestingOrderRefund geneticTestingOrderRefund : geneticTestingOrderRefunds) {
            RefundApprovalRecordDto refundApprovalRecordDto = new RefundApprovalRecordDto();
            BigDecimal refundMoney = geneticTestingOrderRefund.getRefundMoney();
            if (refundMoney != null) {
                refundApprovalRecordDto.setRefundAmount(Double.valueOf(refundMoney.toString()));
            } else {
                refundApprovalRecordDto.setRefundAmount(0.00);
            }
            refundApprovalRecordDto.setAuditDate(geneticTestingOrderRefund.getUpdateDateTime());
            refundApprovalRecordDto.setAuditor(geneticTestingOrderRefund.getAuditor());
            Integer refundStatusInt = geneticTestingOrderRefund.getRefundStatus();
            String refundStatus = "拒绝";
            if (refundStatusInt == 29) {
                refundStatus = "通过";
            }
            refundApprovalRecordDto.setAction(refundStatus);
            refundApprovalRecordDto.setRejectRefundReason(geneticTestingOrderRefund.getRejectRefundReason());
            refundApprovalRecordDtos.add(refundApprovalRecordDto);
        }
        orderRefundDetailsDto.setRefundApprovalRecordDtos(refundApprovalRecordDtos);
        return orderRefundDetailsDto;
    }

    @Override
    public ApiResult<OrderRefundInfoDto> findOrderRefundInfo(String orderId) {
        FieldChecker.checkEmpty(orderId, "订单id");
        GeneticTestingOrder order = orderRepository.findById(orderId);
        if (order == null || DeleteFlagType.DELETE.equals(DeleteFlagType.getEnum(order.getDeleteFlag()))) {
            return ApiResult.fail(OrderErrorCode.ORDER_NOT_EXISTED);
        }
        ClientType orderOrigin = ClientType.getEnum(order.getOrderOrigin());
        if (orderOrigin.isGeneShop()) {
            return ApiResult.fail(OrderErrorCode.ORDER_REFUND_ORIGIN_NOT_SUPPORTED);
        }
        OrderStatus currentStatus = OrderStatus.getEnum(order.getStatus());
        if (!currentStatus.canRefund()) {
            return ApiResult.fail(OrderErrorCode.ORDER_REFUND_NOT_PERMISSION);
        }
        OrderRefundInfoDto refund = new OrderRefundInfoDto();
        refund.setOrderId(orderId);
        refund.setOrderNo(order.getOrderNo());
        List<GeneticTestingOrderPackage> packages = orderPackageRepository.queryOrderPackages(orderId);
        if (!CollectionUtils.isEmpty(packages)) {
            refund.setRefundPackages(packages.stream().map(e -> {
                OrderRefundPackageDto item = new OrderRefundPackageDto();
                item.setPackageId(e.getPackageId());
                item.setPackageName(e.getPackageName());
                item.setAmount(e.getCharges());
                item.setRefundAmount(BigDecimal.ZERO);
                item.setStatus(e.getStatus());
                item.setStatusDesc(OrderPackageStatus.getEnum(e.getStatus()).getDesc());
                return item;
            }).collect(Collectors.toList()));
        }

        return ApiResult.succ(refund);
    }
}
